Home Blog Portfolio Problems

#5 Reverse Antineering

The following code was ran with an peculiar initial state in place of the comment # unknown starting state

use std::sync::{Arc, atomic::AtomicBool};

const GRID_SIZE: usize = 29;

struct Mem([bool; GRID_SIZE * GRID_SIZE]);

impl Mem {
    fn new(grid: [bool; GRID_SIZE * GRID_SIZE]) -> Self {
        Self(grid)
    }
    fn get(&self, x: u8, y: u8) -> bool {
        if y >= GRID_SIZE as u8 || x >= GRID_SIZE as u8 {
            return false;
        }
        self.0[y as usize * GRID_SIZE + x as usize]
    }
    fn set(&mut self, x: u8, y: u8, val: bool) {
        self.0[y as usize * GRID_SIZE + x as usize] = val;
    }
}

impl Default for Mem {
    fn default() -> Self {
        Self::new([false; GRID_SIZE * GRID_SIZE])
    }
}

#[derive(Clone, Copy, Debug)]
#[repr(u8)]
enum Dir {
    Up,
    Right,
    Down,
    Left,
}

struct Langton {
    x: u8,
    y: u8,
    dir: Dir,
}

impl Langton {
    fn new(x: u8, y: u8, dir: Dir) -> Self {
        Self { x, y, dir }
    }

    fn step(&mut self) {
        match self.dir {
            Dir::Up => {
                self.y = if self.y == 0 {
                    GRID_SIZE as u8 - 1
                } else {
                    self.y - 1
                }
            }
            Dir::Down => self.y = (self.y + 1) % GRID_SIZE as u8,
            Dir::Left => {
                self.x = if self.x == 0 {
                    GRID_SIZE as u8 - 1
                } else {
                    self.x - 1
                }
            }
            Dir::Right => self.x = (self.x + 1) % GRID_SIZE as u8,
        }
    }

    fn turn_right(&mut self) {
        self.dir = match self.dir {
            Dir::Up => Dir::Right,
            Dir::Right => Dir::Down,
            Dir::Down => Dir::Left,
            Dir::Left => Dir::Up,
        }
    }
    fn turn_left(&mut self) {
        self.dir = match self.dir {
            Dir::Right => Dir::Up,
            Dir::Down => Dir::Right,
            Dir::Left => Dir::Down,
            Dir::Up => Dir::Left,
        }
    }
}

fn display(mem: &Mem) {
    let mut res = String::new();
    res.push_str("\x1b[H");
    for y in 0..=(GRID_SIZE as u8 / 2) {
        let mut line = String::new();
        for x in 0..(GRID_SIZE as u8) {
            if mem.get(x, y * 2) {
                line.push_str("\x1b[107m");
            } else {
                line.push_str("\x1b[40m");
            }

            if mem.get(x, y * 2 + 1) {
                line.push_str("\x1b[97m");
            } else {
                line.push_str("\x1b[30m");
            }

            line.push('\u{2584}');
        }
        line.push('\n');
        res.push_str(&line);
    }
    println!("{}", res);
}

fn main() {
    let mut running = Arc::new(AtomicBool::new(true));
    let mut r = running.clone();
    println!("\x1b[2J");
    let mut mem = Mem::new([
        # unkown starting state
    ]);
    let mut langton = Langton::new(GRID_SIZE as u8 / 2, GRID_SIZE as u8 / 2, Dir::Right);
    let mut i = 0;
    while running.load(std::sync::atomic::Ordering::SeqCst) {
        i += 1;
        if mem.get(langton.x, langton.y) {
            langton.turn_left();
            mem.set(langton.x, langton.y, false);
        } else {
            langton.turn_right();
            mem.set(langton.x, langton.y, true);
        };
        langton.step();
        display(&mem);
        println!("\x1b[0m{i}");

        if i == 3000 {
            break;
        }
    }
    println!("\x1b[2J");
    display(&mem);
    println!("\x1b[0m{i}");
    println!("{:?}", langton.dir);
    println!("{};{}", langton.x, langton.y);
}

It's output after finishing looked like this:

Are you able to reverse the execution of the program so that you can see what the initial state was?

The code can also be found here